/**
 * 
 */
package jmine.tec.utils.version;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author takeshi
 */
public class VersionComponent implements Comparable<VersionComponent>, Serializable {

    public static final VersionComponent NONE = new VersionComponent("NONE") {

        public boolean equals(Object obj) {
            return this == NONE;
        }

        public int hashCode() {
            return System.identityHashCode(this);
        }

        protected Object readResolve() {
            return NONE;
        }

        public int compareTo(VersionComponent o) {
            if (this == o) {
                return 0;
            }
            return -1;
        }

    };

    public static final Pattern INT_NUMBER_PATTERN = Pattern.compile("[0-9]+");

    public static final Pattern OTHER_PATTERN = Pattern.compile("[\\D]+");

    private final String versionString;

    /**
     * @param string String
     */
    public VersionComponent(String string) {
        this.versionString = string;
    }

    /**
     * {@inheritDoc}
     */
    public int compareTo(VersionComponent o) {
        if (o == NONE) {
            return 1;
        }
        Iterator<Piece> thisIterator = this.pieces().iterator();
        Iterator<Piece> otherIterator = o.pieces().iterator();
        while (thisIterator.hasNext()) {
            if (!otherIterator.hasNext()) {
                return 1;
            }
            int cmp = thisIterator.next().compareTo(otherIterator.next());
            if (cmp != 0) {
                return cmp;
            }
        }
        if (otherIterator.hasNext()) {
            return -1;
        }
        return 0;
    }

    /**
     * Faz o parse dos pedacos
     * 
     * @return {@link Iterable}
     */
    private Iterable<Piece> pieces() {
        List<Piece> list = new ArrayList<Piece>();
        return this.nextPiece(this.versionString, list);
    }

    /**
     * Faz o parse do proximo pedaco
     * 
     * @param current String
     * @param result List que armazena os resultados
     * @return List
     */
    private List<Piece> nextPiece(String current, List<Piece> result) {
        if (current.length() == 0) {
            return result;
        }
        Matcher matcher = INT_NUMBER_PATTERN.matcher(current);
        if (matcher.lookingAt()) {
            String intPiece = matcher.group();
            String rest = current.substring(intPiece.length());
            result.add(new IntegerPiece(Integer.parseInt(intPiece)));
            return this.nextPiece(rest, result);
        }
        matcher = OTHER_PATTERN.matcher(current);
        if (!matcher.lookingAt()) {
            throw new RuntimeException("unknown string: " + current);
        }
        String group = matcher.group();
        String rest = current.substring(group.length());
        result.add(new StringPiece(group));
        return this.nextPiece(rest, result);
    }

    /**
     * Inner interface just to make comparison easy
     * 
     * @author takeshi
     */
    private static interface Piece extends Comparable<Piece> {
    }

    /**
     * @author takeshi
     */
    private static class IntegerPiece implements Piece {

        private final int value;

        /**
         * @param value int
         */
        public IntegerPiece(int value) {
            this.value = value;
        }

        /**
         * {@inheritDoc}
         */
        public int compareTo(Piece o) {
            if (o instanceof StringPiece) {
                return -1;
            }
            return this.value - ((IntegerPiece) o).value;
        }

    }

    /**
     * @author takeshi
     */
    private static class StringPiece implements Piece {

        private final String value;

        /**
         * @param value String
         */
        public StringPiece(String value) {
            this.value = value;
        }

        /**
         * {@inheritDoc}
         */
        public int compareTo(Piece o) {
            if (o instanceof IntegerPiece) {
                return 1;
            }
            return this.value.compareTo(((StringPiece) o).value);
        }

    }

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#hashCode()
     */

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.versionString == null) ? 0 : this.versionString.hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }
        VersionComponent other = (VersionComponent) obj;
        if (this.versionString == null) {
            if (other.versionString != null) {
                return false;
            }
        } else if (!this.versionString.equals(other.versionString)) {
            return false;
        }
        return true;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.lang.Object#toString()
     */

    @Override
    public String toString() {
        return this.versionString;
    }

}